{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c3e2458f-d038-4845-93a0-d4ad830f9f90",
   "metadata": {},
   "source": [
    "# LangChain 核心模块学习：Chains\n",
    "\n",
    "对于简单的大模型应用，单独使用语言模型（LLMs）是可以的。\n",
    "\n",
    "**但更复杂的大模型应用需要将 `LLMs` 和 `Chat Models` 链接在一起 - 要么彼此链接，要么与其他组件链接。**\n",
    "\n",
    "LangChain 为这种“链式”应用程序提供了 `Chain` 接口。\n",
    "\n",
    "LangChain 以通用方式定义了 `Chain`，它是对组件进行调用序列的集合，其中可以包含其他链。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "be885177-e986-4c77-8fc4-bb0b70bac5d8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 去掉pip install 使用项目依赖版本\n",
    "# ! pip install -U langchain"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f9cf0d43-107b-47ae-9e2c-2edaec38c800",
   "metadata": {},
   "source": [
    "## Chain Class 基类\n",
    "\n",
    "类继承关系：\n",
    "\n",
    "```\n",
    "Chain --> <name>Chain  # Examples: LLMChain, MapReduceChain, RouterChain\n",
    "```\n",
    "\n",
    "**代码实现：https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/chains/base.py**\n",
    "\n",
    "```python\n",
    "# 定义一个名为Chain的基础类\n",
    "class Chain(Serializable, Runnable[Dict[str, Any], Dict[str, Any]], ABC):\n",
    "    \"\"\"为创建结构化的组件调用序列的抽象基类。\n",
    "    \n",
    "    链应该用来编码对组件的一系列调用，如模型、文档检索器、其他链等，并为此序列提供一个简单的接口。\n",
    "    \n",
    "    Chain接口使创建应用程序变得容易，这些应用程序是：\n",
    "    - 有状态的：给任何Chain添加Memory可以使它具有状态，\n",
    "    - 可观察的：向Chain传递Callbacks来执行额外的功能，如记录，这在主要的组件调用序列之外，\n",
    "    - 可组合的：Chain API足够灵活，可以轻松地将Chains与其他组件结合起来，包括其他Chains。\n",
    "    \n",
    "    链公开的主要方法是：\n",
    "    - `__call__`：链是可以调用的。`__call__`方法是执行Chain的主要方式。它将输入作为一个字典接收，并返回一个字典输出。\n",
    "    - `run`：一个方便的方法，它以args/kwargs的形式接收输入，并将输出作为字符串或对象返回。这种方法只能用于一部分链，不能像`__call__`那样返回丰富的输出。\n",
    "    \"\"\"\n",
    "\n",
    "    # 调用链\n",
    "    def invoke(\n",
    "        self, input: Dict[str, Any], config: Optional[runnableConfig] = None\n",
    "    ) -> Dict[str, Any]:\n",
    "        \"\"\"传统调用方法。\"\"\"\n",
    "        return self(input, **(config or {}))\n",
    "\n",
    "    # 链的记忆，保存状态和变量\n",
    "    memory: Optional[BaseMemory] = None\n",
    "    \"\"\"可选的内存对象，默认为None。\n",
    "    内存是一个在每个链的开始和结束时被调用的类。在开始时，内存加载变量并在链中传递它们。在结束时，它保存任何返回的变量。\n",
    "    有许多不同类型的内存，请查看内存文档以获取完整的目录。\"\"\"\n",
    "\n",
    "    # 回调，可能用于链的某些操作或事件。\n",
    "    callbacks: Callbacks = Field(default=None, exclude=True)\n",
    "    \"\"\"可选的回调处理程序列表（或回调管理器）。默认为None。\n",
    "    在对链的调用的生命周期中，从on_chain_start开始，到on_chain_end或on_chain_error结束，都会调用回调处理程序。\n",
    "    每个自定义链可以选择调用额外的回调方法，详细信息请参见Callback文档。\"\"\"\n",
    "\n",
    "    # 是否详细输出模式\n",
    "    verbose: bool = Field(default_factory=_get_verbosity)\n",
    "    \"\"\"是否以详细模式运行。在详细模式下，一些中间日志将打印到控制台。默认值为`langchain.verbose`。\"\"\"\n",
    "\n",
    "    # 与链关联的标签\n",
    "    tags: Optional[List[str]] = None\n",
    "    \"\"\"与链关联的可选标签列表，默认为None。\n",
    "    这些标签将与对这个链的每次调用关联起来，并作为参数传递给在`callbacks`中定义的处理程序。\n",
    "    你可以使用这些来例如识别链的特定实例与其用例。\"\"\"\n",
    "\n",
    "    # 与链关联的元数据\n",
    "    metadata: Optional[Dict[str, Any]] = None\n",
    "    \"\"\"与链关联的可选元数据，默认为None。\n",
    "    这些元数据将与对这个链的每次调用关联起来，并作为参数传递给在`callbacks`中定义的处理程序。\n",
    "    你可以使用这些来例如识别链的特定实例与其用例。\"\"\"\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "5d51fbb4-1d8e-4ec1-8c55-ec70247d4d64",
   "metadata": {},
   "outputs": [],
   "source": [
    "# "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c81a7df0-26c7-4eb8-92f1-cc54445cf507",
   "metadata": {},
   "source": [
    "## LLMChain\n",
    "\n",
    "LLMChain 是 LangChain 中最简单的链，作为其他复杂 Chains 和 Agents 的内部调用，被广泛应用。\n",
    "\n",
    "一个LLMChain由PromptTemplate和语言模型（LLM or Chat Model）组成。它使用直接传入（或 memory 提供）的 key-value 来规范化生成 Prompt Template（提示模板），并将生成的 prompt （格式化后的字符串）传递给大模型，并返回大模型输出。\n",
    "\n",
    "![](../images/llm_chain.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "757a67a6-c1aa-4dde-94ef-fb9865dc634c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# from langchain_openai import OpenAI\n",
    "# from langchain.prompts import PromptTemplate\n",
    "\n",
    "# llm = OpenAI(model_name=\"gpt-3.5-turbo-instruct\", temperature=0.9, max_tokens=500)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "829f5103-0122-448e-a725-0c66148301d0",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 【新增】ChatOpenAI调用方式，使用gpt-4o-mini模型\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain.prompts import PromptTemplate\n",
    "\n",
    "llm = ChatOpenAI(model_name=\"gpt-4o-mini\", temperature=0.9, max_tokens=500)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "0b863511-ee01-43e8-8540-4e3f109a5a1a",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = PromptTemplate(\n",
    "    input_variables=[\"product\"],\n",
    "    template=\"给制造{product}的有限公司取10个好名字，并给出完整的公司名称\",\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "b877560c-cb66-41ad-b484-b2df2a60a00d",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_31241/2131540286.py:3: LangChainDeprecationWarning: The class `LLMChain` was deprecated in LangChain 0.1.17 and will be removed in 1.0. Use :meth:`~RunnableSequence, e.g., `prompt | llm`` instead.\n",
      "  chain = LLMChain(llm=llm, prompt=prompt)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'product': '性能卓越的GPU', 'text': '以下是十个适合制造性能卓越的GPU公司的名称及其完整的公司名称：\\n\\n1. **极限视觉科技有限公司**  \\n   (Limitless Vision Technology Co., Ltd.)\\n\\n2. **超频图形处理有限公司**  \\n   (Overclock Graphics Processing Co., Ltd.)\\n\\n3. **锐视电子科技有限公司**  \\n   (SharpSight Electronics Technology Co., Ltd.)\\n\\n4. **未来显卡制造有限公司**  \\n   (Future GPU Manufacturing Co., Ltd.)\\n\\n5. **动能图形解决方案有限公司**  \\n   (Kinetic Graphics Solutions Co., Ltd.)\\n\\n6. **超性能图形科技有限公司**  \\n   (Ultra Performance Graphics Technology Co., Ltd.)\\n\\n7. **高效计算视觉有限公司**  \\n   (Efficient Computing Vision Co., Ltd.)\\n\\n8. **极致图形创新有限公司**  \\n   (Ultimate Graphics Innovations Co., Ltd.)\\n\\n9. **智绘科技有限公司**  \\n   (SmartRender Technology Co., Ltd.)\\n\\n10. **顶尖图形硬件有限公司**  \\n    (TopTier Graphics Hardware Co., Ltd.)\\n\\n希望这些名称能够激发您的灵感！'}\n"
     ]
    }
   ],
   "source": [
    "from langchain.chains import LLMChain\n",
    "\n",
    "chain = LLMChain(llm=llm, prompt=prompt)\n",
    "print(chain.invoke({\n",
    "    'product': \"性能卓越的GPU\"\n",
    "    }))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "e8c7012e-e37e-4224-ba5f-e7132d16f684",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain.verbose =True"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "916ec42a-002b-4912-a599-a7be909e55ff",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.verbose"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "cfa71d7c-2859-47e1-9815-4be2ec9dbd74",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
      "Prompt after formatting:\n",
      "\u001b[32;1m\u001b[1;3m给制造性能卓越的GPU的有限公司取10个好名字，并给出完整的公司名称\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "{'product': '性能卓越的GPU', 'text': '当然可以！以下是10个适合制造性能卓越的GPU公司的名称及其完整公司名称：\\n\\n1. **极辉图形科技有限公司**  \\n   (Extreme Radiance Graphics Technology Co., Ltd.)\\n\\n2. **瞬绘科技股份有限公司**  \\n   (InstantRender Technology Corporation)\\n\\n3. **超能图形解决方案有限公司**  \\n   (SuperPower Graphics Solutions Co., Ltd.)\\n\\n4. **火焰核心图形有限公司**  \\n   (FlameCore Graphics Ltd.)\\n\\n5. **星际图形引擎有限公司**  \\n   (Stellar Graphics Engine Co., Ltd.)\\n\\n6. **黑曜石视觉科技有限公司**  \\n   (Obsidian Vision Technology Co., Ltd.)\\n\\n7. **锐视图形创新有限公司**  \\n   (SharpView Graphics Innovations Co., Ltd.)\\n\\n8. **幻影图形设计有限公司**  \\n   (Phantom Graphics Design Co., Ltd.)\\n\\n9. **神速图形科技有限公司**  \\n   (Swift Graphics Technology Co., Ltd.)\\n\\n10. **未来图形动力有限公司**  \\n    (Future Graphics Dynamics Co., Ltd.)\\n\\n希望这些名称能激发您的灵感！'}\n"
     ]
    }
   ],
   "source": [
    "print(chain.invoke({\n",
    "    'product': \"性能卓越的GPU\"\n",
    "    }))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ac5411e7-b8ec-4c31-b659-deb44af038df",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "99cbf75e-98f4-4c99-b8a7-9a48cc28c7bc",
   "metadata": {},
   "source": [
    "## Sequential Chain\n",
    "\n",
    "串联式调用语言模型（将一个调用的输出作为另一个调用的输入）。\n",
    "\n",
    "顺序链（Sequential Chain ）允许用户连接多个链并将它们组合成执行特定场景的流水线（Pipeline）。有两种类型的顺序链：\n",
    "\n",
    "- SimpleSequentialChain：最简单形式的顺序链，每个步骤都具有单一输入/输出，并且一个步骤的输出是下一个步骤的输入。\n",
    "- SequentialChain：更通用形式的顺序链，允许多个输入/输出。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e192c8c-49fc-4d04-8444-e6aa6bd7b725",
   "metadata": {},
   "source": [
    "### 使用 SimpleSequentialChain 实现戏剧摘要和评论（单输入/单输出）\n",
    "\n",
    "![](../images/simple_sequential_chain_0.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "a4d192a2-d563-4ab7-979f-640fa34f1914",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这是一个 LLMChain，用于根据剧目的标题撰写简介。\n",
    "# llm = OpenAI(temperature=0.7, max_tokens=1000)\n",
    "\n",
    "# 【新增】ChatOpenAI调用方式，使用gpt-4o-mini模型\n",
    "llm = ChatOpenAI(model_name=\"gpt-4o-mini\", temperature=0.9, max_tokens=500)\n",
    "\n",
    "template = \"\"\"你是一位剧作家。根据戏剧的标题，你的任务是为该标题写一个简介。\n",
    "\n",
    "标题：{title}\n",
    "剧作家：以下是对上述戏剧的简介：\"\"\"\n",
    "\n",
    "prompt_template = PromptTemplate(input_variables=[\"title\"], template=template)\n",
    "synopsis_chain = LLMChain(llm=llm, prompt=prompt_template)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "3f7d429b-7ba7-4643-bd9f-fdb737ebf964",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这是一个LLMChain，用于根据剧情简介撰写一篇戏剧评论。\n",
    "# llm = OpenAI(temperature=0.7, max_tokens=1000)\n",
    "template = \"\"\"你是《纽约时报》的戏剧评论家。根据剧情简介，你的工作是为该剧撰写一篇评论。\n",
    "\n",
    "剧情简介：\n",
    "{synopsis}\n",
    "\n",
    "以下是来自《纽约时报》戏剧评论家对上述剧目的评论：\"\"\"\n",
    "\n",
    "prompt_template = PromptTemplate(input_variables=[\"synopsis\"], template=template)\n",
    "review_chain = LLMChain(llm=llm, prompt=prompt_template)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a5265129-5ccd-4e29-b221-0ec24eb84c2b",
   "metadata": {},
   "source": [
    "![](../images/simple_sequential_chain_1.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "de4d816e-16e1-4382-9064-6c03e5841ea2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这是一个SimpleSequentialChain，按顺序运行这两个链\n",
    "from langchain.chains import SimpleSequentialChain\n",
    "\n",
    "overall_chain = SimpleSequentialChain(chains=[synopsis_chain, review_chain], verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "d503ac4f-e337-4436-86a1-7fd937efb06a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new SimpleSequentialChain chain...\u001b[0m\n",
      "\u001b[36;1m\u001b[1;3m简介：\n",
      "\n",
      "在一个不远的未来，人类与外星文明“三体人”展开了一场前所未有的生存斗争。故事围绕一群来自不同背景的地球人，他们各自在面对三体人带来的挑战中，逐渐揭示出人类的脆弱与坚韧。随着战斗的升级，主体人物们经历了失落、背叛与希望的多重考验，在面对强大敌人时，他们开始质疑自身的信仰与选择。\n",
      "\n",
      "剧中不仅描绘了宏大的宇宙对决，更深刻探讨了人性、合作与牺牲。人类在绝望中寻找团结的力量，从历史的教训中汲取智慧。随着剧情的发展，角色们发现，真正的敌人不只是外来者，还有内心的恐惧与疑虑。最终，他们意识到，三体人并不是无法战胜的，关键在于人类如何团结一致、发挥智慧、迎接挑战。\n",
      "\n",
      "通过紧张激烈的情节与深刻的哲理思考，本剧探讨了科技进步带来的伦理困境，以及在生存的边缘，人类所能展现的勇气与信念。\u001b[0m\n",
      "\u001b[33;1m\u001b[1;3m**标题：在黑暗中寻找光明——《三体：人类的回应》评论**\n",
      "\n",
      "在当今充满不确定性的时代，剧院的舞台成为了我们审视人性、信仰与生存的窗口，而新剧《三体：人类的回应》则以其深邃的叙事与强大的情感冲击，将观众带入一个不远的未来，探讨人类在绝境中所能展现出的脆弱与坚韧。\n",
      "\n",
      "剧作的设定围绕着与外星文明“三体人”的生存斗争，在这一设定中，编剧以不同背景的角色描绘了人类在面对巨大威胁时的共鸣与冲突。每一个角色都带着自身的故事背景与人性复杂性，随着剧情的发展，他们的失落、背叛和希望逐渐交织在一起，展现出人类最真实的面貌。这种切入点不仅让人感到亲切，也反映了当代社会对合作与牺牲的深刻思考。\n",
      "\n",
      "本剧的高光时刻并不局限于紧张的宇宙对抗场面，反而是在人物内心的挣扎与成长中绽放光芒。导演巧妙地利用舞台艺术，借助灯光与音响的变化，营造出一种紧迫感，让观众感受到角色们在面对内心恐惧时的无助与渴求团结的心声。演员们的表现可圈可点，尤其是饰演主角的演员，他/她将角色在战斗中的勇敢与内心的脆弱表现得淋漓尽致，令人印象深刻。\n",
      "\n",
      "剧中对科技进步带来的伦理困境的探讨，尤其引人深思。在这个信息爆炸的时代，科技不仅改变了人类的生活方式，也让我们面临新的道德挑战。剧中角色们在面对外敌时的矛盾，正是对我们当下社会中个体与集体、技术与伦理之间关系的深刻反思。\n",
      "\n",
      "然而，《三体：人类的回应》不仅仅是一部科幻剧，它更像是一面镜子，\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "review = overall_chain.invoke(\"三体人不是无法战胜的\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "ce4d75e1-8c57-4583-be7d-60a3488e35b5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new SimpleSequentialChain chain...\u001b[0m\n",
      "\u001b[36;1m\u001b[1;3m**简介：星球大战第九季**\n",
      "\n",
      "在宇宙的深处，银河的命运再度悬于一线。在经历了一系列激烈的斗争和情感纠葛后，抵抗军与第一秩序的最终对决即将展开。九季的故事延续了传奇角色的命运，同时引入了一批崭新的英雄和反派。在这一季中，莱雅公主演绎着领导者的崇高与孤独，瑞伊则在掌握原力与寻求自我认同之间徘徊，芬恩和波达重新定义了友谊与牺牲的意义。\n",
      "\n",
      "面对新的敌人——一个神秘的古老势力，抵抗军必须团结一致，探索宇宙深处的失落遗迹和神秘的力量。与此同时，旧恩怨与新的联盟交织，角色之间的关系更加复杂与紧张。在战斗、背叛与救赎的交错中，正义与邪恶的界限变得模糊。\n",
      "\n",
      "《星球大战第九季》不仅是一场史诗般的视觉盛宴，也是一段关于希望、勇气与爱的旅程。每一次的选择都将在这一伟大的战斗中留下不可磨灭的印记，观众将被带入一个充满惊奇与挑战的星际冒险，见证银河系的命运如何在这一季中被重新书写。\u001b[0m\n",
      "\u001b[33;1m\u001b[1;3m**评论：在星际之间的拼搏与救赎——《星球大战第九季》**\n",
      "\n",
      "在时光流转、星际穿梭的宇宙中，抵抗军与第一秩序的最终对决如同一颗超新星，绚烂又不可避免地吸引每一双眼睛。而在这一强烈的视觉冲击背后，《星球大战第九季》以其深邃的情感与复杂的人性，再次将我们带入了那片神秘的银河。\n",
      "\n",
      "本季的开场便是一场引人入胜的宇宙战争，火光与激光交错，令人窒息的战斗场面无不彰显出制片组对特效的精雕细琢。然而，正是角色间的深厚羁绊和内心斗争更为打动人心。莱雅公主的孤独与负担，瑞伊在自我探索中的挣扎，芬恩与波达对友情的重新定义，所有这一切都让我们在热血沸腾的战斗之外，感受到了人性深处的脆弱与坚韧。\n",
      "\n",
      "尤为突出的是莱雅公主这一角色的塑造。作为领导者，她不仅要面对外敌的威胁，更要承受内心的孤独与责任。她那种无畏的坚持与柔软的内心交错，让观众在她的身上看到了领导力背后的真实情感，使她成为了本季的灵魂所在。\n",
      "\n",
      "瑞伊的角色发展同样值得关注。她在寻求力量与自我认同之间的徘徊，恰如许多年轻人在成长过程中面对的困惑。她的愈发成熟与坚定，提醒观众在面对未知时，真实的自我才是最终的力量源泉。\n",
      "\n",
      "但值得注意的是，新引入的敌人和古老势力的设定，虽然为剧情增添了新的冲突和悬念，却在某些情节上显得略为单薄，缺乏能够与主线相提并论的深度。虽然战斗场景极具视觉冲击力，角色之间的情感关系却在一定程度上\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "review = overall_chain.invoke(\"星球大战第九季\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5fe32f1d-475d-4211-9b32-0c66dd8bff01",
   "metadata": {},
   "source": [
    "### 使用 SequentialChain 实现戏剧摘要和评论（多输入/多输出）\n",
    "\n",
    "![](../images/sequential_chain_0.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "2a04d84f-15c6-4a8d-a4db-200dfa405afa",
   "metadata": {},
   "outputs": [],
   "source": [
    "# # 这是一个 LLMChain，根据剧名和设定的时代来撰写剧情简介。\n",
    "# llm = OpenAI(temperature=.7, max_tokens=1000)\n",
    "\n",
    "# # 【新增】ChatOpenAI调用方式，使用gpt-4o-mini模型\n",
    "llm = ChatOpenAI(model_name=\"gpt-4o-mini\", temperature=0.9, max_tokens=500)\n",
    "\n",
    "template = \"\"\"你是一位剧作家。根据戏剧的标题和设定的时代，你的任务是为该标题写一个简介。\n",
    "\n",
    "标题：{title}\n",
    "时代：{era}\n",
    "剧作家：以下是对上述戏剧的简介：\"\"\"\n",
    "\n",
    "prompt_template = PromptTemplate(input_variables=[\"title\", \"era\"], template=template)\n",
    "# output_key\n",
    "synopsis_chain = LLMChain(llm=llm, prompt=prompt_template, output_key=\"synopsis\", verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "250afe66-e014-4097-9798-f9ba812023fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 这是一个LLMChain，用于根据剧情简介撰写一篇戏剧评论。\n",
    "\n",
    "template = \"\"\"你是《纽约时报》的戏剧评论家。根据该剧的剧情简介，你需要撰写一篇关于该剧的评论。\n",
    "\n",
    "剧情简介：\n",
    "{synopsis}\n",
    "\n",
    "来自《纽约时报》戏剧评论家对上述剧目的评价：\"\"\"\n",
    "\n",
    "prompt_template = PromptTemplate(input_variables=[\"synopsis\"], template=template)\n",
    "review_chain = LLMChain(llm=llm, prompt=prompt_template, output_key=\"review\", verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "9eb46f6d-841b-4b87-9ed5-a5913ef9aec5",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chains import SequentialChain\n",
    "\n",
    "m_overall_chain = SequentialChain(\n",
    "    chains=[synopsis_chain, review_chain],\n",
    "    input_variables=[\"era\", \"title\"],\n",
    "    # Here we return multiple variables\n",
    "    output_variables=[\"synopsis\", \"review\"],\n",
    "    verbose=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "5a4a12ef-da2a-42ad-8044-fb71aedd3e2d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new SequentialChain chain...\u001b[0m\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
      "Prompt after formatting:\n",
      "\u001b[32;1m\u001b[1;3m你是一位剧作家。根据戏剧的标题和设定的时代，你的任务是为该标题写一个简介。\n",
      "\n",
      "标题：三体人不是无法战胜的\n",
      "时代：二十一世纪的新中国\n",
      "剧作家：以下是对上述戏剧的简介：\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "\n",
      "\n",
      "\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
      "Prompt after formatting:\n",
      "\u001b[32;1m\u001b[1;3m你是《纽约时报》的戏剧评论家。根据该剧的剧情简介，你需要撰写一篇关于该剧的评论。\n",
      "\n",
      "剧情简介：\n",
      "**剧名：三体人不是无法战胜的**\n",
      "\n",
      "**时代：二十一世纪的新中国**\n",
      "\n",
      "**简介：**\n",
      "\n",
      "在一个科技飞速发展的新时代，中国正迎来前所未有的挑战与机遇。外星文明“三体人”通过深空信号一路而来，带着对地球的强烈好奇与潜在威胁。与此同时，地球上的人类社会因强烈的竞争与资源争夺变得愈加分裂。政府、科学家与普通民众在面对即将到来的外星入侵时，各自展现出不同的反应与态度。\n",
      "\n",
      "故事的主线围绕一位年轻的科学家李明展开。他是三体问题研究的先锋，试图通过技术与智慧破解三体人能够瞬间摧毁地球文明的秘密。李明不仅要面对外星科技的复杂性，更要解决自己内心的恐惧和对人类命运的思考。在这个过程中，他结识了勇敢坚韧的网络安全专家张慧和对外星文化充满好奇的学生小李，他们共同组成了一个跨学科的团队，力图寻找与三体人和平共处的可能性。\n",
      "\n",
      "随着剧情的发展，团队发现三体人并非不可战胜，相反，他们也有着自己的困境与痛苦。在多次尝试与交流后，李明团队逐渐领悟到，真正的胜利并不在于击败敌人，而是在于建立理解与合作。通过一系列感人的互动与智谋较量，剧中展现了人类在面对未知时的脆弱与坚韧，信任与背叛，还有友谊与希望。\n",
      "\n",
      "在大决战的高潮时刻，李明带领团队运用科学与人性的力量，向三体人传递出人类共同生存的信念，最终促成了两种文明之间的理解与合作，证明了即便在绝境中，智慧与勇气依然能够开辟出一条光明的未来。\n",
      "\n",
      "这部戏剧不仅是一场外星文明的科幻冒险，更是一幅描绘人类情感与智慧的深刻画卷，揭示了在面对强大未知力量时，\n",
      "\n",
      "来自《纽约时报》戏剧评论家对上述剧目的评价：\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'title': '三体人不是无法战胜的',\n",
       " 'era': '二十一世纪的新中国',\n",
       " 'synopsis': '**剧名：三体人不是无法战胜的**\\n\\n**时代：二十一世纪的新中国**\\n\\n**简介：**\\n\\n在一个科技飞速发展的新时代，中国正迎来前所未有的挑战与机遇。外星文明“三体人”通过深空信号一路而来，带着对地球的强烈好奇与潜在威胁。与此同时，地球上的人类社会因强烈的竞争与资源争夺变得愈加分裂。政府、科学家与普通民众在面对即将到来的外星入侵时，各自展现出不同的反应与态度。\\n\\n故事的主线围绕一位年轻的科学家李明展开。他是三体问题研究的先锋，试图通过技术与智慧破解三体人能够瞬间摧毁地球文明的秘密。李明不仅要面对外星科技的复杂性，更要解决自己内心的恐惧和对人类命运的思考。在这个过程中，他结识了勇敢坚韧的网络安全专家张慧和对外星文化充满好奇的学生小李，他们共同组成了一个跨学科的团队，力图寻找与三体人和平共处的可能性。\\n\\n随着剧情的发展，团队发现三体人并非不可战胜，相反，他们也有着自己的困境与痛苦。在多次尝试与交流后，李明团队逐渐领悟到，真正的胜利并不在于击败敌人，而是在于建立理解与合作。通过一系列感人的互动与智谋较量，剧中展现了人类在面对未知时的脆弱与坚韧，信任与背叛，还有友谊与希望。\\n\\n在大决战的高潮时刻，李明带领团队运用科学与人性的力量，向三体人传递出人类共同生存的信念，最终促成了两种文明之间的理解与合作，证明了即便在绝境中，智慧与勇气依然能够开辟出一条光明的未来。\\n\\n这部戏剧不仅是一场外星文明的科幻冒险，更是一幅描绘人类情感与智慧的深刻画卷，揭示了在面对强大未知力量时，',\n",
       " 'review': '**剧评：在星际碰撞中探索人性——《三体人不是无法战胜的》**\\n\\n在当今这个科技飞速发展的时代，面对无穷无尽的未知，《三体人不是无法战胜的》以其深邃的思考和感染力，成为一部富有张力和哲理的戏剧作品。由年轻的科学家李明与他的团队展开的外星文明探索，不仅仅是对三体人入侵的回应，更是对人类自身脆弱与坚韧、理解与合作的深刻反思。\\n\\n剧作通过紧凑的剧情和细腻的角色刻画，将观众引入一个紧张的局面。李明作为主角，既代表了科学探索的前沿，也承载了对人类命运的深切思考。他与张慧和小李间的互动，既展现了团结合作的重要性，也强调了跨学科知识结合的必要性。在他们共同面对三体人这一“未知”的过程中，观众不仅感受到科技的魅力，更被人物间真挚的情感所打动。\\n\\n值得注意的是，剧中的三体人并非单纯的“敌人”，他们的困境与痛苦使得整个故事更加复杂与人性化。剧作通过一系列感人的交流与冲突，深入探讨了相互理解的重要性，打破了对“外星人”的刻板印象。这种处理不仅让人感受到戏剧的深度，也反映出当今社会在面对外部挑战时所需的智慧与勇气。\\n\\n高潮部分，李明团队以科学和人性的力量向三体人传递“共同生存”的信念，展现了人类的希望与勇敢。这不仅是一次戏剧性的胜利，更是对未来可能性的展望。在这种意义上，该剧不仅仅是科幻的冒险，更是一种人文主义的抒发，呼唤观众对生命价值的深入思考。\\n\\n在舞台呈现上，剧中利用了现代化的舞台技术与音效，营造出一种科幻与现实交错的氛围，为观众提供了沉浸式的观剧体验。视觉'}"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m_overall_chain.invoke({\"title\":\"三体人不是无法战胜的\", \"era\": \"二十一世纪的新中国\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8c20cf4e-25b4-453d-9f7a-84138ca25cf8",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "22b5071b-3e5d-44ba-aa91-d75c07ba40f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "#### The class `LLMChain` was deprecated in LangChain 0.1.17 and will be removed in 1.0. Use :meth:`~RunnableSequence, e.g., `prompt | llm`` instead.\n",
    "### 【新增】langchain 0.3版本，使用RunnableSequence替换LLMChain，并指定 output_key\n",
    "\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import PromptTemplate\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_core.runnables import RunnableSequence\n",
    "\n",
    "llm = ChatOpenAI(\n",
    "    model_name=\"gpt-3.5-turbo\",\n",
    "    temperature=0,\n",
    "    max_tokens=200\n",
    ")\n",
    "\n",
    "summarizing_prompt_template = \"\"\"\n",
    "总结以下文本为一个 20 字以内的句子:\n",
    "---\n",
    "{content}\n",
    "\"\"\"\n",
    "summarizing_prompt = PromptTemplate.from_template(summarizing_prompt_template)\n",
    "summarizing_chain = summarizing_prompt | llm | StrOutputParser()\n",
    "\n",
    "translating_prompt_template = \"\"\"\n",
    "将{summary}翻译成英文:\n",
    "\"\"\"\n",
    "translating_prompt = PromptTemplate.from_template(translating_prompt_template)\n",
    "translating_chain = translating_prompt | llm | StrOutputParser()\n",
    "\n",
    "# Construct a RunnableSequence with custom output keys\n",
    "overall_chain = summarizing_chain | {\n",
    "    'summary': summarizing_chain,\n",
    "    'translation': translating_chain\n",
    "}\n",
    "\n",
    "test_contetent = \"\"\"\n",
    "端到端在深度学习中指的是一种模型架构设计理念：\n",
    "从原始输入数据到最终输出结果，整个决策过程完全由单一神经网络完成，无需人工设计中间处理环节。\n",
    "这种设计摒弃了传统分步骤、模块化的处理流程，让模型自主挖掘数据中隐藏的复杂关联。\n",
    "\"\"\"\n",
    "\n",
    "result = overall_chain.invoke({\"content\": test_contetent})\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b6836f0-213d-4cac-abc9-3617831be3db",
   "metadata": {},
   "source": [
    "### Homework\n",
    "\n",
    "#### 使用 OutputParser 优化 overall_chain 输出格式，区分 synopsis_chain 和 review_chain 的结果"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1c7edb0a-675d-40c0-9f5d-d58f0170ce72",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
